/*
 * ActualEESTraderApi.cpp
 *
 *  Created on: 2021��8��19��
 *      Author: root
 */
#include <arpa/inet.h>
#include "ActualEESTraderApi.h"

ActualEESTraderApi::ActualEESTraderApi(map<string, string> params, DataEngine *p_engine) {
    this->p_engine = p_engine;
    InputParam(params);
    bool res = Init();
    if (!res) {
        cout << "init faild" << endl;
    }

}

ActualEESTraderApi::~ActualEESTraderApi() {
    if (p_order != nullptr) {
        delete p_order;
        p_order = nullptr;
    }

    this->Linux_UnloadEESTrader();

}

bool ActualEESTraderApi::Init() {
    bool ret = this->Linux_LoadEESTrader();
    if (!ret) {
        return false;
    }
    EES_TradeSvrInfo svrInfo;
    strcpy(svrInfo.m_remoteTradeIp, m_tradeServerIp.c_str());
    svrInfo.m_remoteTradeTCPPort = m_tradeServerPort;
    svrInfo.m_remoteTradeUDPPort = m_tradeServerUDPPort;
    strcpy(svrInfo.m_remoteQueryIp, m_queryServerIp.c_str());
    svrInfo.m_remoteQueryTCPPort = m_queryServerPort;
    strcpy(svrInfo.m_LocalTradeIp, m_udpLocalIp.c_str());
    svrInfo.m_LocalTradeUDPPort = m_udpLocalPort;
    svrInfo.m_TcpdirectSwitch= true;
    svrInfo.m_RspCpuId=10;
    RESULT ret_err = m_tradeApi->ConnServer(svrInfo, this, true);

    if (ret_err != NO_ERROR) {
        cout << ret_err << endl;
        printf("connect to REM server failed!\n");
        return false;
    }

    int waitTime = 0; //�ȴ���ʱ
    while (m_logonStatus != 2 && m_logonStatus != 3) {
        sleep(1);
        waitTime++;
        if (waitTime >= 5) //5�볬ʱ
        {
            printf("wait for logon response timeout!\n");
            return false;
        }
    }

    return (2 == m_logonStatus);

}

RESULT ActualEESTraderApi::ChangePassword(const char *oldPwd, const char *newPwd) {
    return this->m_tradeApi->ChangePassword(oldPwd, newPwd);
}

void ActualEESTraderApi::OnConnection(ERR_NO errNo, const char *pErrStr) {
    if (errNo != NO_ERROR) {
        printf("connect to rem server failed(%d), %s!\n", errNo, pErrStr);
        return;
    }
    m_logonStatus = 1;
    Logon();
}

void ActualEESTraderApi::OnQueryMarketSession(EES_ExchangeMarketSession *pMarketSession, bool bFinish) {

    for (int i = 0; i < int(pMarketSession->m_SessionCount); ++i) {
        SPDLOG_INFO("INFO:EXCHANGEID = {}  SESSION {}", int(pMarketSession->m_ExchangeID),
                    int(pMarketSession->m_SessionId[i]));
    }

}

void ActualEESTraderApi::OnDisConnection(ERR_NO errNo, const char *pErrStr) {
    printf("disconnect from rem server(%d), %s!\n", errNo, pErrStr);
    m_logonStatus = 3;
}

void ActualEESTraderApi::OnUserLogon(EES_LogonResponse *pLogon, MY_SOCKET m_socket) {

    if (pLogon->m_Result != NO_ERROR) {
        m_logonStatus = 3;
        printf("logon failed, result=%d\n", pLogon->m_Result);
        return;
    }
    p_engine->m_MaxToken = pLogon->m_MaxToken;
    p_engine->getNextToken();
    m_logonStatus = 2;

    this->m_userId = pLogon->m_UserId;
    p_engine->traderDay = pLogon->m_TradingDate;
    cout << "query sessionid" << endl;
    this->m_socketUdp = m_socket;
    bzero(&m_remote_addr, sizeof(m_remote_addr));
    m_remote_addr.sin_family = AF_INET;
    m_remote_addr.sin_addr.s_addr = inet_addr(m_tradeServerIp.c_str());
    m_remote_addr.sin_port = htons(m_tradeServerUDPPort);
    printf("logon successfully, trading date(%u), max token(%d) (socket id)%d\n", pLogon->m_TradingDate,
           pLogon->m_MaxToken, m_socketUdp);
           
    this->m_tradeApi->QueryMarketSession();
    this->m_tradeApi->QuerySymbolList();
}

void ActualEESTraderApi::OnUserLogon(EES_LogonResponse *pLogon) {

    if (pLogon->m_Result != NO_ERROR) {
        m_logonStatus = 3;
        printf("logon failed, result=%d\n", pLogon->m_Result);
        return;
    }
    p_engine->m_MaxToken = pLogon->m_MaxToken;
    p_engine->getNextToken();
    m_logonStatus = 2;
    this->m_userId = pLogon->m_UserId;
    printf("logon successfully, trading date(%u), max token(%d)\n", pLogon->m_TradingDate, pLogon->m_MaxToken);
    cout << "query sessionid" << endl;
    this->m_tradeApi->QueryMarketSession();

}

bool ActualEESTraderApi::Linux_LoadEESTrader() {

    m_handle = dlopen(EES_TRADER_DLL_NAME, RTLD_LAZY);
    if (!m_handle) {
        printf("load library(%s) failed.\n", EES_TRADER_DLL_NAME);
        return false;
    }

    auto createFun = (funcCreateEESTraderApi) dlsym(m_handle, CREATE_EES_TRADER_API_NAME);
    if (!createFun) {
        printf("get function addresss(%s) failed!\n", CREATE_EES_TRADER_API_NAME);
        return false;
    }

    m_distoryFun = (funcDestroyEESTraderApi) dlsym(m_handle, DESTROY_EES_TRADER_API_NAME);
    if (!createFun) {
        printf("get function addresss(%s) failed!\n", DESTROY_EES_TRADER_API_NAME);
        return false;
    }

    m_tradeApi = createFun();
    m_tradeApi->SetLoggerSwitch(false);
    if (!m_tradeApi) {
        printf("create trade API object failed!\n");
        return false;
    }

    return true;
}

void ActualEESTraderApi::Linux_UnloadEESTrader() {

    if (m_tradeApi) {
        m_distoryFun(m_tradeApi);
        m_tradeApi = nullptr;
        m_distoryFun = nullptr;
    }

    if (m_handle) {
        dlclose(m_handle);
        m_handle = nullptr;
    }

}

/*void ActualEESTraderApi::InputParam() {
 m_tradeServerIp = "10.79.250.22";
 m_tradeServerPort = 20000;
 m_tradeServerUDPPort = 19999;
 m_queryServerIp = "10.79.250.21";
 m_queryServerPort = 20001;
 m_udpLocalIp = "10.179.250.34";
 m_udpLocalPort = 20001;
 m_logonId = "999871021";
 m_logonPwd = "13338805197AB";
 m_appid = "client_sqzc_1.1";
 m_account = "999871021";
 m_authCode = "P5XIZDBBLZ1FWLDY";

 }*/

void ActualEESTraderApi::InputParam(map<string, string> &params) {
    m_tradeServerIp = "10.79.250.22";
    m_tradeServerPort = 20000;
    m_tradeServerUDPPort = 19999;
    m_queryServerIp = "10.79.250.21";
    m_queryServerPort = 20001;
    m_udpLocalPort = 19997;

    m_udpLocalIp = params["localIp"];
    m_logonId = params["loginId"];
    m_logonPwd = params["loginPwd"];
    m_appid = params["appid"];
    m_account = params["account"];
    m_authCode = params["authCode"];

}

/*void ActualEESTraderApi::InputParam() {
 m_tradeServerIp = "10.15.48.60";
 m_tradeServerPort = 20000;
 m_tradeServerUDPPort = 19999;
 m_queryServerIp = "10.15.48.61";
 m_queryServerPort = 20001;
 m_udpLocalIp = "10.15.48.61";
 m_udpLocalPort = 20001;
 m_logonId = "20089876";
 m_logonPwd = "223183";
 m_appid = "client_sqzc_1.1";
 m_account = "20089876";
 m_authCode = "XH50LEADGHKKGHOM";
 }*/


void ActualEESTraderApi::Logon() {
    if (!m_tradeApi) {
        printf("INVALID api object\n");
        return;
    }

    m_tradeApi->UserLogon(m_logonId.c_str(), m_logonPwd.c_str(), m_appid.c_str(), m_authCode.data());
}

void ActualEESTraderApi::OnOrderAccept(EES_OrderAcceptField *pAccept) {
/*    if (!isBind) {
        cpu_set_t mask;
        CPU_ZERO(&mask);
        CPU_SET(10, &mask);
        pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);
        this->isBind = true;
    }*/
    if (pAccept->m_UserID != this->m_userId || pAccept->m_ClientOrderToken <= 0) {
        return;
    }
    tmpEvent.order.initByOrderAccept(pAccept);
    p_engine->addEvent(tmpEvent);
}

void ActualEESTraderApi::OnOrderReject(EES_OrderRejectField *pReject) {
    if (pReject->m_Userid != this->m_userId || pReject->m_ClientOrderToken <= 0) {
        return;
    }

    auto it=tokenContractMap.find(pReject->m_ClientOrderToken);
    if(it!=tokenContractMap.end())
    {
        tmpEvent.order.initByOrderReject(it->second, pReject);
        p_engine->addEvent(tmpEvent);
    }
    else
    {
        SPDLOG_ERROR("not have this clientToken {}", pReject->m_ClientOrderToken);
    }

}

void ActualEESTraderApi::OnOrderMarketAccept(EES_OrderMarketAcceptField *pAccept) {
    if (pAccept->m_UserID != this->m_userId) {
        return;
    }

    auto it=tokenContractMap.find(pAccept->m_ClientOrderToken);
    if(it!=tokenContractMap.end())
    {
        tmpEvent.order.initByMarketAccept(it->second, pAccept);
        p_engine->addEvent(tmpEvent);
    }
    else
    {
        SPDLOG_ERROR("not have this clientToken {}", pAccept->m_ClientOrderToken);
    }


}

void ActualEESTraderApi::OnOrderMarketReject(EES_OrderMarketRejectField *pReject) {
    if (pReject->m_UserID != this->m_userId) {
        return;
    }
    auto it=tokenContractMap.find(pReject->m_ClientOrderToken);
    if(it!=tokenContractMap.end())
    {
        tmpEvent.order.initByMarketReject(it->second, pReject);
        p_engine->addEvent(tmpEvent);
    }
    else
    {
        SPDLOG_ERROR("not have this clientToken {}", pReject->m_ClientOrderToken);
    }


}

void ActualEESTraderApi::OnOrderExecution(EES_OrderExecutionField *p_exeField) {
    if (p_exeField->m_Userid != this->m_userId) {
        return;
    }
    auto it=tokenContractMap.find(p_exeField->m_ClientOrderToken);
    if(it!=tokenContractMap.end())
    {
        tmpEvent.order.initByOrderExecution(it->second, p_exeField);
        p_engine->addEvent(tmpEvent);
    }
    else
    {
        SPDLOG_ERROR("not have this clientToken {}", p_exeField->m_ClientOrderToken);
    }

}

void ActualEESTraderApi::OnOrderCxled(EES_OrderCxled *pCxled) {
    if (pCxled->m_Userid != this->m_userId) {
        return;
    }
    auto it=tokenContractMap.find(pCxled->m_ClientOrderToken);
    if (it!=tokenContractMap.end())
    {
        tmpEvent.order.initByOrderCxled(it->second, pCxled);
        p_engine->addEvent(tmpEvent);
    }
    else
    {
        SPDLOG_ERROR("not have this clientToken {}", pCxled->m_ClientOrderToken);
    }

}

void ActualEESTraderApi::OnCxlOrderReject(EES_CxlOrderRej *pReject) {
    if (pReject->m_UserID != this->m_userId) {
        return;
    }
    auto it=tokenContractMap.find(pReject->m_ClientOrderToken);
    if (it!=tokenContractMap.end())
    {
        tmpEvent.order.initByCxlReject(it->second, pReject);
        p_engine->addEvent(tmpEvent);
    } else{
        SPDLOG_ERROR("not have this clientToken {}", pReject->m_ClientOrderToken);
    }
}

void ActualEESTraderApi::OnSymbolStatusReport(EES_SymbolStatus *pSymbolStatus) {
    if (strcmp(pSymbolStatus->m_EnterTime, last_EnterTime) != 0) {
        if(strcmp(pSymbolStatus->m_EnterTime, "08:55:00") == 0||strcmp(pSymbolStatus->m_EnterTime, "20:55:00") == 0)
        {
            strncpy(origOrderField.m_Account,m_account.c_str(), sizeof(origOrderField.m_Account));
            origOrderField.m_MarketOrderToken=0;
            origOrderField.m_OrigUserId=m_userId;
            m_tradeApi->EnterOrigOrder(&origOrderField);
        }

        int hour, minute, second;
        utils::splitTime(pSymbolStatus->m_EnterTime, hour, minute, second);
        p_engine->lastOffset = -(DataEngine::calculateTime(hour, minute, second));

        if (strcmp(pSymbolStatus->m_EnterTime, "08:55:00") == 0) {
            this->m_tradeApi->QueryAccountOrder(this->m_account.c_str());
        }
        if (strcmp(pSymbolStatus->m_EnterTime, "02:30:00") == 0) {
            p_engine->saveCancelMap();
            p_engine->stopAllStrategy();
            this->m_tradeApi->QueryUserAccount();
            sleep(1);
            this->m_tradeApi->QueryAccountOrder(this->m_account.c_str());
        }
        if (strcmp(pSymbolStatus->m_EnterTime, "15:00:00") == 0) {
            p_engine->resetCancelMap();
            p_engine->resetPosition();
            p_engine->saveCancelMap();
            p_engine->stopAllStrategy();
            this->m_tradeApi->QueryUserAccount();
            sleep(1);
            this->m_tradeApi->QueryAccountOrder(this->m_account.c_str());
        }
        this->p_engine->processSymbolStatusReport(pSymbolStatus);
        p_engine->traderTime = to_string(p_engine->traderDay).append(" ").append(pSymbolStatus->m_EnterTime);

        RedisImp redis("127.0.0.1");
        redis.getRedis().hset("noticeOffset", p_engine->traderTime, to_string(p_engine->lastOffset));
        p_engine->enterTime_map[pSymbolStatus->m_EnterTime].add(p_engine->lastOffset);
        //redis.getRedis().hset("notice",string(pSymbolStatus->m_EnterTime),p_engine->enterTime_map[string(last_EnterTime)].toJson());
        strncpy(last_EnterTime, pSymbolStatus->m_EnterTime, 9);
    }
}

void ActualEESTraderApi::OnQuerySymbolStatus(EES_SymbolStatus *pSymbolStatus, bool bFinish) {
    if (pSymbolStatus != nullptr) {
        this->p_engine->processQrySymbolStatusReport(pSymbolStatus, bFinish);
    }
}

void ActualEESTraderApi::OnQueryUserAccount(EES_AccountInfo *pAccoutnInfo, bool bFinish) {
    if (bFinish) {

    } else {
        if (pAccoutnInfo != nullptr) {
            p_engine->InitialBp=pAccoutnInfo->m_InitialBp;
            m_InitialBp = pAccoutnInfo->m_InitialBp;
            m_CurrentBp = pAccoutnInfo->m_AvailableBp + pAccoutnInfo->m_Margin + pAccoutnInfo->m_FrozenCommission +
                          pAccoutnInfo->m_FrozenMargin;
            m_Fee = pAccoutnInfo->m_CommissionFee;
        }
    }
}

void ActualEESTraderApi::OnQuerySymbol(EES_SymbolField *pSymbol, bool bFinish) {
    if (bFinish) {
        RedisImp redis("127.0.0.1");

        for (const auto &contract: contractList) {
            json j;
            j["EES_Symbol"] = contract.m_symbol;
            j["EES_ExchangeID"] = contract.m_ExchangeID;
            j["m_VolumeMultiple"] = contract.m_VolumeMultiple;
            j["m_PriceTick"] = contract.m_PriceTick;
            j["m_ExpireDate"] = contract.m_ExpireDate;
            if (!redis.getRedis().hexists("contract", contract.m_symbol)) {
                redis.getRedis().hset("contract", contract.m_symbol, j.dump());
            }

        }
    } else {
        if (pSymbol->m_SecType == 3) {
            contractList.push_back(*pSymbol);
        }

    }

}

void ActualEESTraderApi::OnQueryTradeOrder(const char *pAccount, EES_QueryAccountOrder *pQueryOrder, bool bFinish) {
    if (bFinish) {
        if (strcmp(last_EnterTime, "02:30:00") == 0 || strcmp(last_EnterTime, "15:00:00") == 0) {

            this->m_tradeApi->QueryAccountOrderExecution(this->m_account.c_str());
        }
    } else {
        if (pQueryOrder != nullptr) {
            std::bitset<8> res(pQueryOrder->m_OrderStatus);
            if (strcmp(last_EnterTime, "08:55:00") == 0) {
                if (res[0] != EES_OrderStatus_closed) {
                    ReqOrderMsg orderMsg(pQueryOrder->m_ClientOrderToken, pQueryOrder->m_MarketOrderToken);
                    tokenContractMap.insert(make_pair(pQueryOrder->m_ClientOrderToken, pQueryOrder->m_symbol));

                    p_engine->udpOrderHandel(orderMsg);
                }
            }
            if (strcmp(last_EnterTime, "02:30:00") == 0 || strcmp(last_EnterTime, "15:00:00") == 0) {
                tm tm_now{};
                unsigned int res = 0;
                m_tradeApi->ConvertFromTimestamp(pQueryOrder->m_Timestamp, tm_now, res);
                char buffer[30];
                strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_now);
                SPDLOG_INFO("{} {} {} {} {} {}", buffer, pQueryOrder->m_ClientOrderToken, pQueryOrder->m_symbol,
                            pQueryOrder->m_Price, pQueryOrder->m_SideType, pQueryOrder->m_Quantity);
            }
        }
    }
}

void ActualEESTraderApi::OnQueryTradeOrderExec(const char *pAccount, EES_QueryOrderExecution *pQueryOrderExec,
                                               bool bFinish) {
    if (strcmp(last_EnterTime, "02:30:00") == 0 || strcmp(last_EnterTime, "15:00:00") == 0) {
        if (!bFinish) {
            if (pQueryOrderExec != nullptr) {
                this->sumVol = this->sumVol + pQueryOrderExec->m_ExecutedQuantity;

            }
        } else {
            if (strcmp(last_EnterTime, "02:30:00") == 0) {
                SPDLOG_ERROR("EXIT:{} trade {} {} {} {}", last_EnterTime, this->sumVol, m_InitialBp, m_CurrentBp,
                             m_Fee);
                spdlog::default_logger()->flush();
                exit(0);
            }
            if (strcmp(last_EnterTime, "15:00:00") == 0) {
                SPDLOG_ERROR("EXIT:{} trade {} {} {} {}", last_EnterTime, this->sumVol, m_InitialBp, m_CurrentBp,
                             m_Fee);
                spdlog::default_logger()->flush();
/*                RedisImp redisImp("127.0.0.1");
                for(auto it:p_engine->strategies)
                {
                    j["dealVol"]=it.second->dealVol;
                    j["sumProfit"]=it.second->sumProfit;
                    j["strategyName"]=it.second->strategyName;
                    j["contractCodes"]=it.second->contractCodes;

                }
                redisImp.getRedis().hset("Transaction", to_string(p_engine->traderDay),);*/
                //exit(0);
            }
        }

    }
}

void ActualEESTraderApi::startUdpOrderThread() {
    if (p_order == nullptr) {
        p_order = new thread([this]() {

            cpu_set_t mask;
            CPU_ZERO(&mask);
            CPU_SET(11, &mask);
            pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask);

            while (true) {

                if (p_engine->udpBuffer.empty()) {
                    if (p_engine->isStop) {
                        break;
                    }
                    //m_tradeApi->EnterOrderWarm();
                } else {
                    BufConsumer<ReqOrderMsg> my_bufConsumer(&p_engine->udpBuffer);
                    ReqOrderMsg &field = my_bufConsumer.GetContent();
                    if (field.type == INSERT) {
                        if (field.enterOrder.m_clientToken <= 30000) {
                            strcpy(field.enterOrder.m_account, m_account.c_str());
                            field.enterOrder.m_user_id = m_userId;
                            if (field.enterOrder.m_exchange == EES_ExchangeID_ine) {
                                field.enterOrder.m_mkt_session_id = ineSessions[
                                        field.enterOrder.m_clientToken % 2];
                            } else {
                                field.enterOrder.m_mkt_session_id = shfeSessions[
                                        field.enterOrder.m_clientToken % 4];
                            }
                            tokenContractMap[field.enterOrder.m_clientToken] = field.enterOrder.m_symbol;
                            ssize_t res = sendto(m_socketUdp, (const char *) &field.enterOrder,
                                                 sizeof(field.enterOrder), 0, (struct sockaddr *) &m_remote_addr,
                                                 sizeof(m_remote_addr));
                            if (-1 == res) {
                                Event tmpEvent;
                                tmpEvent.order.initByOrderFailed(field.enterOrder.m_symbol,
                                                                 field.enterOrder.m_price, field.enterOrder.m_quantity,
                                                                 field.enterOrder.m_side,
                                                                 field.enterOrder.m_clientToken);
                                p_engine->addEvent(tmpEvent);
                                SPDLOG_ERROR("ERROR:res = {} clientToken = {}", res, field.enterOrder.m_clientToken);
                            }
                        } else {
                            SPDLOG_ERROR("ERROR: too many send times");
                            //break;
                        }
                    } else if (CANCEL == field.type) {
                        strcpy(field.cxlOrder.m_account, m_account.c_str());
                        field.cxlOrder.m_user_id = m_userId;
                        ssize_t res = sendto(m_socketUdp, (const char *) &field.cxlOrder, sizeof(field.cxlOrder), 0,
                                             (struct sockaddr *) &m_remote_addr, sizeof(m_remote_addr));
                        if (-1 == res) {
                            auto it=tokenContractMap.find(field.cxlOrder.m_clientToken);
                            if(it!=tokenContractMap.end())
                            {
                                Event tmpEvent(tokenContractMap[field.cxlOrder.m_clientToken]);
                                tmpEvent.order.initByCxlFailed(field.cxlOrder, res);
                                p_engine->addEvent(tmpEvent);
                                SPDLOG_ERROR("CANCEL FAILED res={}  token = {}", res,
                                             field.cxlOrder.m_clientToken);
                            }

                        }
                    }


                }
            }

        });
        p_order->detach();
    }
}

void ActualEESTraderApi::OnOrigOrderReport(EES_OrigOrderReportField *pAccept) {
    if(pAccept->m_ReasonCode==0)
    {
        SPDLOG_INFO("{} {}", pAccept->m_BasketId,pAccept->m_MarketOrderToken);
    } else
    {
        SPDLOG_INFO("错误代码{} 错误：{}", pAccept->m_ReasonCode,pAccept->m_ReasonText);
    }
}





